<!DOCTYPE html>
<html>
<head>
    <title>Codimension Python IDE - Plugins Tutorial</title>
    <meta charset="utf-8">
    <meta name="Author" content="Sergey Satskiy">
    <meta name="description" content="Codimension is a Python IDE with a focus on graphics representation of the control flow">
    <meta name="keywords" content="codimension,python,ide,flowchart,analysis,linux,open source,free software,libre software,libre,freedom,diagram,flowchart,software,download,platform,ubuntu,fedora,debian,documentation,screenshots,home,homepage">

    <link rel="apple-touch-icon" sizes="144x144" href="../assets/cdm/images/apple-touch-icon-144x144.png">
    <link rel="apple-touch-icon" sizes="114x114" href="../assets/cdm/images/apple-touch-icon-114x114.png">
    <link rel="apple-touch-icon" sizes="72x72" href="../assets/cdm/images/apple-touch-icon-72x72.png">
    <link rel="apple-touch-icon" sizes="57x57" href="../assets/cdm/images/apple-touch-icon-57x57.png">
    <link rel="shortcut icon" type="image/png" href="../assets/cdm/images/cdm-logo-64x64.png">

    <link rel="stylesheet" type="text/css" href="../assets/cdm/css/main.css">
    <link rel="stylesheet" type="text/css" href="../assets/cdm/css/text.css">
    <link rel="stylesheet" type="text/css" href="../assets/cdm/css/github.css">

    <script type="text/javascript" src="../assets/cdm/js/jquery.min.js"></script>
    <script type="text/javascript" src="../assets/cdm/js/cmsplugin_toc.js"></script>

    <script type="text/javascript" src="../assets/cdm/js/highlight.pack.js"></script>
    <script>hljs.initHighlightingOnLoad();</script>
</head>
<body id="page-" class="">
<div class="header">
<div id="cleartop">
    <div id="logo">
        <a href="../index.htm"><img src="../assets/cdm/images/cdm-label.svg" height="64" alt="Codimension"></a>
    </div>

    <div id="header-right">
        <ul id="topdrops">
            <h2 style="border-bottom: 1px solid #ddd; font-size: 140%; font-weight: normal; margin: 1.5ex 0 0.5ex;">English</h2>
        </ul>
        <!--
          <form id="topsearch" action="search-results.html" method="get">
          <input type="text" name="search" id="q" placeholder="Search">
          <button type="submit"><img src="/assets/cdm/images/search.svg" alt="Search"/></button>
          </form>
        -->
    </div>
</div>
<div class="nav">
<ul id="menu"><li class="child"><a href="../index.htm">Home</a></li>
<li class="child"><a href="../about/index.htm">About</a><ul><li class="child"><a href="../about/features.html">Features</a></li>
<li class="child"><a href="../about/screenshots.html">Screenshots</a></li>
<li class="child"><a href="../about/team.html">Team</a></li>
</ul></li>
<li class="child"><a href="../download/index.htm">Download</a><ul><li class="child"><a href="../download/linuxdownload.html">Linux and Mac Download and Installation</a></li>
<li class="child"><a href="../download/sourcedownload.html">Download Source Code</a></li>
<li class="child"><a href="../download/runfromgit.html">Building and Running from Source</a></li>
</ul></li>
<li class="child ancestor"><a href="index.htm">Documentation</a><ul><li class="child"><a href="visualization-technology/index.htm">Visualization Technology</a></li>
<li class="child"><a href="faq.html">FAQ</a></li>
<li class="child"><a href="cheatsheet.html">Key Bindings & Cheatsheet</a></li>
<li class="child selected"><a href="pluginstutorial.html">Plugins Tutorial</a></li>
<li class="child"><a href="cdmpyparser.html">Brief Python Parser</a></li>
<li class="child"><a href="cdmflowparser.html">Control Flow Parser</a></li>
<li class="child"><a href="codimension-ide-architecture.html">Architecture</a></li>
</ul></li>
<li class="child"><a href="../contribute/index.htm">Contribute</a><ul><li class="child"><a href="../contribute/codingcontribute.html">Via Working with Code</a></li>
<li class="child"><a href="../contribute/noncodingcontribute.html">Non-Coding</a></li>
</ul></li>
<li class="child"><a href="../supportus.html">Support Us</a></li>
</ul>
</div>

</div>
<div id="content">
<div class="wrapper">
<div class="breadcrumbs"><a href="../index.htm">Home</a>&nbsp;&nbsp;&raquo;&nbsp;&nbsp;<a href="index.htm">Documentation</a>&nbsp;&nbsp;&raquo;&nbsp;&nbsp;Plugins Tutorial</div>
<h1>Plugins Tutorial</h1>

<p>
<div id="toc" class="inline" data-bullets="False">
    <h2>Codimension Plugins</h2>
</div>


<h2 id="Intro">Intro</h2>
<p>In this tutorial two major topics are discussed. The first part discusses how plugin
   support is implemented in Codimension. The second part discusses implementation of a
   simple plugin and what is available for plugins.</p>
   
<h2 id="Plugin_Support_Implementation">Plugin Support Implementation</h2>
<p>The shortest answer on the question "what is a Codimension plugin?" is as follows:
   a Codimension plugin is a Python class implemented in a certain way.
   Codimension is written in Python and thus its plugins should also be written in Python.
</p>
<p>Before the implementation details are discussed it makes sense to introduce a few terms
   Codimension uses while it works with plugins.
</p>
<p>At the start time Codimension looks for plugins in two places. The first one 
   is <code>/usr/share/codimension-plugins/</code>. The second one is the directory called
   <code>.codimension/plugins</code> located in the user home directory. So on Linux system
   the latter most probably will be <code>~/.codimension/plugins</code>. It is highly
   recommended that each plugin occupies a designated directory where it keeps all the
   required files. So on a certain system the related directories structure may look as
   follows:
</p>

<div>
    <pre>
/usr/share/codimension-plugins/plugin1/...
                               plugin2/...
                               plugin3/...

/home/mike/.codimension/plugins/plugin4/...
                                plugin5/...
                                plugin6/...
</pre>
</div>

<p>Depending on a plugin location Codimension splits all the found plugins into two groups:
   system wide plugins and user plugins.
   So in the example above <code>plugin1</code>, <code>plugin2</code> and
   <code>plugin3</code> are <b>system wide</b> plugins while
   <code>plugin4</code>, <code>plugin5</code> and <code>plugin6</code> are
   <b>user</b> plugins.
</p>

<p>The next pieces which are important for Codimension are a plugin name and a plugin version.
   A name and a version are stored in a plugin description file (it will be discussed later).
   That description file is what triggers loading a plugin.
</p>

<p>The next important piece is that each plugin can be enabled or disabled (a pair of other
   terms activated/deactivated is also used further with the same meaning). While loading
   plugins Codimension initially treats all plugins enabled so a newly installed plugin will
   be automatically enabled next time Codimension starts. It is possible however that a
   plugin conflicts with another plugin. Certain types of conflicts can be detected by
   Codimension automatically and Codimension can disable some plugins to resolve a conflict.
   The following rules are used for automatic conflict resolution:
   <ul>
        <li>If there is a user and a system wide plugin with the same name then the user
           plugin wins.</li>
        <li>If there are two plugins with the same name and both of them are either user
            or system wide then their versions are taken into consideration. The higher
            version wins.</li>
        <li>If names, versions and locations of two plugins match then an arbitrary one
            wins.</li>
   </ul>


<p>There are a few other cases when Codimension disables a plugin automatically.
   A good example of such a case is when a plugin does not implement the required
   interface.
</p>

<p>An important detail on the plugin initialization stage is that regardless whether a
   plugin is enabled or disabled it is instantiated. The plugin class instance will stay
   in memory till Codimension is closed.
</p>

<p>The user is always able to enable or disable plugins manually and in particular
   resolve detected conflicts the required way if automatic resolution is not what is
   needed. The manual control of plugin states is done in the plugin manager. The manager
   user interface is available via main menu <b>Options->Plugin Manager</b> menu item as
   shown below.
</p>

<img class="centered" src='../assets/cdm/images/pluginstutorial/pluginmanager.png' title=''>
<p style="text-align:center">Figure 1. Plugin Manager</p>

<p>Each plugin can move between the enabled and disabled state an arbitrary number of
   times within a single Codimension session. This could be illustrated as follows.
</p>

<img class="centered" src='../assets/cdm/images/pluginstutorial/pluginstates.png' title=''>
<p style="text-align:center">Figure 2. Plugin States</p>

<p>The last term Codimension introduces for plugins is a plugin category. Plugins
   could require different support on the IDE side and a plugin category is the way how
   to distinguish the required support. For example, a spell checker plugin might need
   certain support targeted to text editing while a plugin which implements a regular
   expression visual testing facility does not need text editing support at all. A plugin
   category defines an interface variation between Codimension and a plugin. The categories
   come in a form of predefined base classes and each plugin must derive from one of them.
</p>

<h2 id="Plugin_Files">Plugin Files</h2>
<p>As it was mentioned above it is highly recommended that a plugin occupies a designated
   directory. For example, directory content for a plugin may look as follows:
</p>

<div>
<pre>/home/mike/.codimension/plugins/pdfexporter/pdfexporter.cdmp
                                            __init__.py
                                            util_functions.py
                                            config_dialog.py
</pre>
</div>

<p>The <code>pdfexporter.cdmp</code> file contains a textual plugin description. The name
   of the file does not matter, Codimension looks for the .cdmp file extensions. A content
   of the <code>pdfexporter.cdmp</code> file may be similar to the following.
</p>

<pre><code class="ini">[Core]
Name = PDF exporter
Module = .

[Documentation]
Author = Mike Slartibartfast <mike.slartibartfast@some.com>
Version = 1.0.0
Website = http://mike.slartibartfast.homelinux.com/pdfexporter
Description = Codimension PDF exporter plugin
License = GPL v.3
</mike.slartibartfast@some.com></code></pre>

<p>The <code>[Core].Name</code> value is an arbitrary string however it is better to keep
   it relatively short. The <code>[Core].Module</code> value is a directory path where
   Codimension plugin resides. It is recommended that all the plugin files are sitting in
   a designated directory including the plugin description file and therefore the
   <code>[Core].Module</code> value refers to the very directory it is sitting in.
   The '.' value is the recommended value for all the Codimension plugins.
</p>

<p>The <code>[Documentation]</code> section has self explanatory values. A plugin can add
   any values to this section and all of them will be displayed in the <b>Detailed information</b>
   box in the plugin manager dialog when a plugin is selected.
</p>

<p>The <code>__init__.py</code> file is the one where a plugin class definition must reside.
   In the example above the plugin also has some utility functions in the
   <code>util_functions.py</code> and a configuration dialog in separate files. To import
   <code>util_functions</code> and <code>config_dialog</code> modules in
   <code>__init__.py</code> there is no need to use relative imports.
   The <code>__init__.py</code> can simply use:
</p>

<div>
<pre><code class="python"># The plugin modules do not require relative import
import config_dialog
from util_functions import designCoastline
</code></pre>
</div>

<p>The Codimension modules are also available for the plugin code. So a plugin can use
   statements similar to the following:
</p>

<div>
<pre><code class="python"># Importing pixmaps cache from a Codimension module
from utils.pixmapcache import PixmapCache
codimensionLogo = PixmapCache().getPixmap( 'logo.png' )
</code></pre>
</div>

<p>It was mentioned in the previous section that a plugin class must derive from one of
   the predefined plugin category base class. So a part of the PDF exporter plugin class
   hierarchy may look as follows:
</p>

<img class="centered" src='../assets/cdm/images/pluginstutorial/pluginbases.png' title=''>
<p style="text-align:center">Figure 3.  Plugin Base Classes</p>

<p>The <code>PDFExporterPlugin</code> class must reside in the <code>__init__.py</code> file.
   This is the class which implements the plugin interface. The plugin developer does not need
   and should not make any changes in any other classes shown on the diagram.</p>

<p>The <code>WizardInterface</code> class is a Codimension provided plugin category base class.
   The class is defined in <code>codimension/src/plugins/categories/wizardiface.py</code>. The class
   has a set of member functions some of which have to be implemented by the plugin of this category.
   The member function documentation strings describe in details what is expected by Codimension.
   At the time of writing (Codimension v.3.0.0) there are two categories supported:
   <ul>
       <li>Generic plugin category (<code>WizardInterface</code>)</li>
       <li>Version control system category (<code>VersionControlSystemInterface</code>)</li>
   </ul>
   When a new plugin category is introduced its base class will appear in the
   <code>codimension/src/plugins/categories/</code> directory.

<p>The <code>CDMPluginBase</code> class is a Codimension provided convenience class which simplifies
   access to the major IDE objects. The class definition resides in the
   <code>codimension/src/plugins/categories/cdmpluginbase.py</code> file.
   Having <code>CDMPluginBase</code> class in the hierarchy makes it possible for a plugin class to use
   simple to read statements similar to the following:</p>

<div>
<pre><code class="python">if self.ide.project.isLoaded():
    # The ide has a project loaded
    ...
else:
    # There is no project, the user edits individual files
    ...
</code></pre>
</div>

<p>Access to all the IDE objects should start with:</p>

<div>
<pre><code class="python">self.ide. ...
</code></pre>
</div>

<p>See the <code>IDEAccess</code> class in the <code>codimension/src/plugins/categories/cdmpluginbase.py</code>
   file for a full list of provided IDE objects.</p>

<p>Codimension uses thirdparty library called <code>yapsy</code> to build plugin support on top of it.
   <code>Yapsy</code> needs to have <code>IPlugin</code> in the plugin class hierarchy and so it is here.
   A plugin developer should not need to deal with this class directly however.</p>

<p>The <code>QObject</code> class is a <code>PyQt</code> provided class. The class is included into
   the hierarchy for convenience. Codimension uses QT library for the user interface and therefore QT
   signals are used quite often. Having <code>QObject</code> in the base gives a convenient way to
   subscribe for signals and to emit them, e.g. a plugin may have the following code:</p>

<div>
<pre><code class="python">self.ide.project.projectChanged.connect( self.__onProjectChanged )
</code></pre>
</div>

<h2 id="Garbage_Collector_Plugin">Plugin Example: Garbage Collector Plugin</h2>

<p>The idea of an example plugin is quite simple. The Python garbage collector triggers objects
   collection at pretty much unknown moments and the plugin will make it more predictable. The garbage
   collector plugin (GC plugin) will call the <code>collect()</code> method of
   the <code>gc</code> Python module when:
<ul>
    <li>a tab is closed</li>
    <li>a project is changed</li>
    <li>new files appeared in a project</li>
    <li>some files are deleted from a project</li>
</ul>


<p>The <code>gc.collect()</code> call provides an information of how many objects were
   collected and this could be interesting to see. So a message should be shown somewhere.
   To make it more user friendly the GC plugin should provide a configuration dialog with options
   where to show the message:
<ul>
    <li>in the log tab</li>
    <li>on the status bar</li>
    <li>do not show anything</li>
</ul>


<p>The selected option should be memorized and restored the next time Codimension starts.</p>

<p>Having the requirements at hand let's start implementing the GC plugin with creating a directory
   where all the plugin files will be located.</p>

<div>
<pre><code class="bash">$ mkdir garbagecollector
$ cd garbagecollector
</code></pre>
</div>

<p>First, we need the plugin description file, let's call it <code>garbagecollector.cdmp</code>.
   The content of the file will be as follows:</p>

<div>
<pre><code class="ini">[Core]
Name = Garbage collector
Module = .

[Documentation]
Author = Sergey Satskiy <sergey.satskiy@gmail.com>
Version = 1.0.0
Website = http://satsky.spb.ru/codimension
Description = Codimension garbage collector plugin
License = GPL v.3
</sergey.satskiy@gmail.com></code></pre>
</div>

<p>The GC plugin will belong to the wizard plugin category so it must derive from the 
   <code>WizardInterface</code> class. The definition of the class must be in
   the <code>__init__.py</code> file:</p>

<div>
<pre><code class="python">from plugins.categories.wizardiface import WizardInterface

class GCPlugin( WizardInterface ):
    def __init__( self ):
        WizardInterface.__init__( self )
        return
</code></pre>
</div>

<p>Codimension instantiates all the found plugins regardless whether they are activated or not.
   So the GC plugin <code>__init__</code> does not do any significant resource consuming initializations.</p>

<p>One of the first things Codimension does before a plugin is acivated, it asks the plugin if the
   current IDE version is supported by the plugin. Codimension passes the current version as a string,
   e.g. "2.1.0". The method must be implemented by the plugin and for the GC plugin it is trivial,
   all the versions are supported:</p>

<div>
<pre><code class="python">    @staticmethod
    def isIDEVersionCompatible( ideVersion ):
        return True
</code></pre>
</div>

<p>The next pair of methods which must be implemented in a plugin is <code>activate</code>
   and <code>deactivate</code>. Obviously, the <code>activate</code> method will be called when
   a plugin is activated. It may happened at the start time automatically or when a user
   activates previously deactivated plugin. Therefore it is generally a good idea to have
   plugin data allocated and deallocated in these two methods respectively.</p>

<p>The first thing to be done in the <code>activate</code> method is to call <code>activate</code> of
   the interface base class. If this call is forgotten then <code>self.ide.</code> statements 
   will not work at all and the plugin management system may also be confused. Respectively,
   the <code>deactivate</code> method should call <code>deactivate</code> of the interface base
   class as the last thing to do.</p>

<p>The GC plugin initialization is basically connecting a few IDE signals with the plugin member
   functions. When the plugin is deactivated the signals should be disconnected.</p>

<div>
<pre><code class="python">    def activate( self, ideSettings, ideGlobalData ):
        WizardInterface.activate( self, ideSettings, ideGlobalData )

        self.connect( self.ide.editorsManager, SIGNAL( 'tabClosed' ),
                      self.__collectGarbage )
        self.connect( self.ide.project, SIGNAL( 'projectChanged' ),
                      self.__collectGarbage )
        return

    def deactivate( self ):
        self.disconnect( self.ide.project, SIGNAL( 'projectChanged' ),
                         self.__collectGarbage )
        self.disconnect( self.ide.editorsManager, SIGNAL( 'tabClosed' ),
                         self.__collectGarbage )

        WizardInterface.deactivate( self )
        return
</code></pre>
</div>

<p>The GC plugin needs some configuring. The user should be able to instruct the plugin
   where the garbage collection information should be displayed. Configuration dialog is going
   to be a modal graphics one with three radio buttons:</p>
   
<img class="centered" src='../assets/cdm/images/pluginstutorial/gcconfigdialog.png' title=''>
<p style="text-align:center">Figure 4.  GC Plugin Configuration Dialog</p>

<p>Let's place the dialog code into a separate file <code>configdlg.py</code>. Three integer
   constants will be defined in the file as well. These constants identify the GC plugin information
   message destination.</p>

<p>Codimension provides a unified way to call plugin configuration dialogs. Look at the plugin
   manager screenshot above. There is a button with a wrench on it for each found plugin.
   If a plugin needs configuring then its button will be enabled.</p>

<p>In order to tell Codimension if a plugin needs configuring there is an interface method which
   should return a python callable or <code>None</code> if no configuring is required.
   The GC plugin needs configuring so the implementation will look as follows:</p>

<div>
<pre><code class="python">from configdlg import GCPluginConfigDialog

    # ...

    def getConfigFunction( self ):
        return self.configure

    def configure( self ):
        dlg = GCPluginConfigDialog( ... )       # Will be discussed below
        if dlg.exec_() == QDialog.Accepted:
            # Will be discussed below
            pass
        return
</code></pre>
</div>

<p>The user choice should be stored to be used next time Codimension starts. There are many
   options how to do it and only one of them is considered here. The user choice will be
   stored in file <code>gc.plugin.conf</code> which uses an industry standard ini files format.
   Where to keep this file? The plugin message destination choice does not depend on a project
   so it does not make sense to store <code>gc.plugin.conf</code> in a project specific data
   directory. It makes sense to store the file where IDE stores its settings. We'll need a few
   member functions to deal with the user choice and one member variable. The member variable
   will be initialized in the plugin class constructor with "do not show anything".</p>

<div>
<pre><code class="python">import ConfigParser

class GCPlugin( WizardInterface ):

    def __init__( self ):
        WizardInterface.__init__( self )
        self.__where = GCPluginConfigDialog.SILENT
        return

    def __getConfigFile( self ):
        return self.ide.settingsDir + 'gc.plugin.conf'

    def __getConfiguredWhere( self ):
        try:
            config = ConfigParser.ConfigParser()
            config.read( [ self.__getConfigFile() ] )
            value = int( config.get( 'general', 'where' ) )
            if value < GCPluginConfigDialog.SILENT or \
               value > GCPluginConfigDialog.LOG:
                return GCPluginConfigDialog.SILENT
            return value
        except:
            return GCPluginConfigDialog.SILENT

    def __saveConfiguredWhere( self ):
        try:
            f = open( self.__getConfigFile(), 'w' )
            f.write( '# Autogenerated GC plugin config file\n'
                     '[general]\n'
                     'where=' + str( self.__where ) + '\n' )
            f.close()
        except:
            pass
</code></pre>
</div>

<p>At the time of the plugin activation the saved value should be restored so we need
   to insert into the activate method (after initializing the plugin base class) the following:</p>

<div>
<pre><code class="python">        self.__where = self.__getConfiguredWhere()
</code></pre>
</div>

<p>Now we can complete implementation of the configuration function:</p>

<div>
<pre><code class="python">    def configure( self ):
        dlg = GCPluginConfigDialog( self.__where )
        if dlg.exec_() == QDialog.Accepted:
            newWhere = dlg.getCheckedOption()
            if newWhere != self.__where:
                self.__where = newWhere
                self.__saveConfiguredWhere()
        return
</code></pre>
</div>

<p>Having the destination of the information message at hand we can implement
   the <code>__collectGarbage</code> method:</p>
   
<div>
<pre><code class="python">import logging

    # ...

    def __collectGarbage( self, ignored = None ):
        iterCount = 0
        collected = 0

        currentCollected = gc.collect()
        while currentCollected > 0:
            iterCount += 1
            collected += currentCollected
            currentCollected = gc.collect()

        if self.__where == GCPluginConfigDialog.SILENT:
            return

        message = "Collected " + str( collected ) + " objects in " + \
                  str( iterCount ) + " iteration(s)"
        if self.__where == GCPluginConfigDialog.STATUS_BAR:
            # Display it for 5 seconds
            self.ide.showStatusBarMessage( message, 5000 )
        else:
            logging.info( message )
        return
</code></pre>
</div>

<p>The last piece we need to discuss is menus. Codimension provides four convenient places where
   a plugin can inject its menu items:

    <ul>
    <li>Main menu. If a plugin provides a main menu item then it is shown in the Codimension main
        menu under the plugin manager menu item. The name of the plugin menu item is set by default
        to the plugin name from the description file however the plugin can change it.</li>
    <li>Editing buffer context menu. If a plugin provides an editing buffer context menu then it is
        shown at the bottom of the standard context menu. The plugin menu item name policy is the same
        as for the main menu.</li>
    <li>Project / file system context menu appeared for a file. It works similar to the editing
        buffer context menu.</li>
    <li>Project / file system context menu appeared for a directory. It works similar to the editing
        buffer context menu.</li>
    </ul>


<p>In all the cases Codimension provides an already created parent menu item in which a plugin can
   populate its menu items. If nothing is populated then Codimension will not display the plugin menu.
   All the menu populating members must be implemented by a plugin.</p>

<p>The GC plugin will have only the main menu. The entries will be for collecting garbage immediately
   and for an alternative way to run the plugin configuration dialog:</p>


<div>
<pre><code class="python">    def populateMainMenu( self, parentMenu ):
        parentMenu.addAction( "Configure", self.configure )
        parentMenu.addAction( "Collect garbage", self.__collectGarbage )
        return

    def populateFileContextMenu( self, parentMenu ):
        # No file context menu is required
        return

    def populateDirectoryContextMenu( self, parentMenu ):
        # No directory context menu is required
        return

    def populateBufferContextMenu( self, parentMenu ):
        return
</code></pre>
</div>

<p>The methods above is a convenient way to deal with context menus for most of the cases.
   Generally speaking plugins are not limited with what they can do because all the IDE objects
   are available via the global data and settings objects passed in the <code>activate</code> method.</p>

<p>The configuration dialog code is not discussed here because it is pure PyQt code and is not specific to the Codimension plugin subsystem.</p>

<p>Full plugin source code is available here:
<ul>
    <li><a href="https://github.com/SergeySatskiy/cdm-gc-plugin/blob/master/cdmplugins/gc/garbagecollector.cdmp">garbagecollector.cdmp</a></li>
    <li><a href="https://github.com/SergeySatskiy/cdm-gc-plugin/blob/master/cdmplugins/gc/__init__.py">__init__.py</a></li>
    <li><a href="https://github.com/SergeySatskiy/cdm-gc-plugin/blob/master/cdmplugins/gc/configdlg.py">configdlg.py</a></li>
</ul>


    
<h2 id="Miscellaneous">Miscellaneous</h2>

<h3 id="Printing_and_Logging">Printing and Logging</h3>

<p>Plugins are running in Codimension context so everything what is done in Codimension for the
   IDE is applicable to plugins. In particular Codimension intercepts printing to <b>stdout</b>
   and to <b>stderr</b>. If a plugin prints on <b>stdout</b>:</p>

<div>
<pre><code class="python">print "Hi from plugin"
</code></pre>
</div>

<p>then the message will appear in the log tab in black. If a plugin prints on <b>stderr</b>:</p>

<div>
<pre><code class="python">print >> sys.stderr, "ATTENTION"
</code></pre>
</div>

<p>then the message will appear in the log tab in red.</p>

<p>Codimension also defines a logging handler so that the messages will be redirected to the
   log tab, for example:</p>

<div>
<pre><code class="python">import logging
logging.info( "Message" )
</code></pre>
</div>

<p>will lead to a message in the log tab. Codimension can be started with <code>--debug</code> option
   and in this case debug log level will be switched on, otherwise debug log messages
   are suppressed. E.g.</p>

<div>
<pre><code class="python">import logging
logging.error( "Error message" )    # Will be shown regardless of the startup options
logging.debug( "Debug message" )    # Will be shown only if Codimension started as:
                                    # > codimension --debug
</code></pre>
</div>


<h3 id="Globals_and_Settings">Globals and Settings</h3>

<p>When a plugin is activated references to the IDE global data singleton and to the IDE
   settings singleton are passed to the plugin. Using these singletons a plugin can get
   access to pretty much everything in the IDE. It is also possible to cause Codimension
   crash if important data are improperly modified.</p>

<p>The <code>CDMPluginBase</code> class provides syntactic shugar to simplify access to the
   most important IDE objects. The other IDE objects could be accessible using direct access
   to the globals and settings members. If you feel more syntactic shugar should be added to
   <code>CDMPluginBase</code> (or something is not accessible) please feel free to contact
   Sergey Satskiy at <a href="mailto:sergey.satskiy@gmail.com.">sergey.satskiy@gmail.com</a></p>

</div>
</div>
<div class="footer">
    <table width="100%">
        <tr>
            <td>
                Codimension is Free and Open Source Software licensed under the 
                <a href="http://www.gnu.org/licenses/gpl-3.0.html">GPL v3.0</a>
            </td>
            <td align="right">
                <a href="../sitemap.html">Sitemap</a>
            </td>
        </tr>
    </table>
</div>

</body>
</html>
